/**
 *******************************************************************************
 * @file  modem.c
 * @brief This file provides firmware functions to manage the modem.
 @verbatim
   Change Logs:
   Date             Author          Notes
   2019-04-10       Yangjp          First version
 @endverbatim
 *******************************************************************************
 * Copyright (C) 2016, Huada Semiconductor Co., Ltd. All rights reserved.
 *
 * This software is owned and published by:
 * Huada Semiconductor Co., Ltd. ("HDSC").
 *
 * BY DOWNLOADING, INSTALLING OR USING THIS SOFTWARE, YOU AGREE TO BE BOUND
 * BY ALL THE TERMS AND CONDITIONS OF THIS AGREEMENT.
 *
 * This software contains source code for use with HDSC
 * components. This software is licensed by HDSC to be adapted only
 * for use in systems utilizing HDSC components. HDSC shall not be
 * responsible for misuse or illegal use of this software for devices not
 * supported herein. HDSC is providing this software "AS IS" and will
 * not be responsible for issues arising from incorrect user implementation
 * of the software.
 *
 * Disclaimer:
 * HDSC MAKES NO WARRANTY, EXPRESS OR IMPLIED, ARISING BY LAW OR OTHERWISE,
 * REGARDING THE SOFTWARE (INCLUDING ANY ACCOMPANYING WRITTEN MATERIALS),
 * ITS PERFORMANCE OR SUITABILITY FOR YOUR INTENDED USE, INCLUDING,
 * WITHOUT LIMITATION, THE IMPLIED WARRANTY OF MERCHANTABILITY, THE IMPLIED
 * WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE OR USE, AND THE IMPLIED
 * WARRANTY OF NONINFRINGEMENT.
 * HDSC SHALL HAVE NO LIABILITY (WHETHER IN CONTRACT, WARRANTY, TORT,
 * NEGLIGENCE OR OTHERWISE) FOR ANY DAMAGES WHATSOEVER (INCLUDING, WITHOUT
 * LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION,
 * LOSS OF BUSINESS INFORMATION, OR OTHER PECUNIARY LOSS) ARISING FROM USE OR
 * INABILITY TO USE THE SOFTWARE, INCLUDING, WITHOUT LIMITATION, ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL OR CONSEQUENTIAL DAMAGES OR LOSS OF DATA,
 * SAVINGS OR PROFITS,
 * EVEN IF Disclaimer HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 * YOU ASSUME ALL RESPONSIBILITIES FOR SELECTION OF THE SOFTWARE TO ACHIEVE YOUR
 * INTENDED RESULTS, AND FOR THE INSTALLATION OF, USE OF, AND RESULTS OBTAINED
 * FROM, THE SOFTWARE.
 *
 * This software may be replicated in part or whole for the licensed use,
 * with the restriction that this Disclaimer and Copyright notice must be
 * included with each copy of this software, whether used in part or whole,
 * at all times.
 *******************************************************************************
 */

/*******************************************************************************
 * Include files
 ******************************************************************************/
#include "hc32_ddl.h"
#include "modem.h"

/**
 * @addtogroup HC32M120_DDL_Examples
 * @{
 */

/**
 * @defgroup MODEM MODEM
 * @{
 */

/*******************************************************************************
 * Local type definitions ('typedef')
 ******************************************************************************/

/*******************************************************************************
 * Local pre-processor symbols/macros ('#define')
 ******************************************************************************/

/*******************************************************************************
 * Global variable definitions (declared in header file with 'extern')
 ******************************************************************************/
__ALIGN_BEGIN static uint8_t u8FrameData[FRAME_SHELL_SIZE + PACKET_MAX_SIZE];

/*******************************************************************************
 * Local function prototypes ('static')
 ******************************************************************************/

/*******************************************************************************
 * Local variable definitions ('static')
 ******************************************************************************/

/*******************************************************************************
 * Function implementation - global ('extern') and local ('static')
 ******************************************************************************/
/**
 * @defgroup MODEM_Global_Functions MODEM Global Functions
 * @{
 */

/**
 * @brief  Calculate CRC16 for Packet
 * @param  [in] pu8Data                 Pointer to the buffer which the data to be calculate.
 * @param  [in] u32Size                 Buffer length.
 * @retval uint16_t                     Calculate result.
 */
static uint16_t Calculate_CRC16(const uint8_t* pu8Data, uint32_t u32Size)
{
    uint16_t u16InitVal = 0xA28C;

    return CRC_Calculate(CRC_CRC16, pu8Data, u16InitVal, u32Size, CRC_BW_8);
}

/**
 * @brief  Modem receive byte.
 * @param  [out] u8RxData               Pointer to the received data.
 * @retval An en_result_t enumeration value:
 *           - Ok: Receive data finished
 *           - Error: Don't receive data
 */
static en_result_t Modem_RecvByte(uint8_t *u8RxData)
{
    if (Set == USART_GetFlag(USART_UNIT, (USART_FLAG_PE | USART_FLAG_FE | USART_FLAG_ORE)))
    {
        USART_ClearFlag(USART_UNIT, (USART_CLEAR_FLAG_PE | USART_CLEAR_FLAG_FE | USART_CLEAR_FLAG_ORE));
    }

    /* Warit Rx data register no empty */
    if (Set == USART_GetFlag(USART_UNIT, USART_FLAG_RXNE))
    {
        *u8RxData = USART_RecData(USART_UNIT);
        return Ok;
    }

    return Error;
}

/**
 * @brief  Modem send data
 * @param  [in] u8TxBuff                Pointer to the buffer which the data to be sent.
 * @param  [in] u16Length               Buffer length.
 * @retval None
 */
static void Modem_SendData(uint8_t *u8TxBuff, uint16_t u16Length)
{
    while (u16Length--)
    {
        USART_SendData(USART_UNIT, *u8TxBuff++);
        /* Wait Tx data complete */
        while (Reset == USART_GetFlag(USART_UNIT, USART_FLAG_TC))
        {
        }
    }
}

/**
 * @brief  Modem receive data
 * @param  [out] u8RxBuff               Pointer to the buffer which the data to be received.
 * @param  [in]  u16Length              Buffer length.
 * @param  [in]  u32Timeout             Receive timeout(ms).
 * @retval An en_result_t enumeration value:
 *           - Ok: Receive data finished
 *           - Error: USART runtime error
 */
static en_result_t Modem_RecvData(uint8_t *u8RxBuff, uint16_t u16Length, uint32_t u32Timeout)
{
    uint8_t u8Temp;
    en_result_t enRet;
    uint16_t u16Cnt = 0;
    uint32_t u32PreValue;

    if ((NULL == u8RxBuff) || (u16Length == 0u))
    {
        return Error;
    }

    for (u16Cnt=0; u16Cnt < u16Length; u16Cnt++)
    {
        u32PreValue = SysTick_GetTick();
        while (SysTick_GetTick() <= (u32PreValue + FRAME_RECV_TIMEOUT))
        {
            enRet = Modem_RecvByte(&u8Temp);
            if (Ok == enRet)
            {
                u8RxBuff[u16Cnt] = u8Temp;
                break;
            }
        }
        if (enRet != Ok)
        {
            return Error;
        }
    }

    return Ok;
}

/**
 * @brief  Modem receive frame
 * @param  [out] u8RxBuff               Pointer to the buffer which the data to be received.
 * @param  [out] u16RxLength            Packet size.
 * @param  [in]  u32Timeout             Receive timeout(ms).
 * @retval An en_result_t enumeration value:
 *           - Ok: Receive finished
 *           - Error: Receive error
 */
en_result_t Modem_RecvFrame(uint8_t *u8RxBuff, uint16_t *u16RxLength, uint32_t u32Timeout)
{
    uint16_t u16Head, u16Crc16;
    uint16_t u16PacketSize;

    *u16RxLength = 0u;
    if (Ok == Modem_RecvData((uint8_t *)&u8RxBuff[FRAME_HEAD_INDEX], 2, u32Timeout))
    {
        u16Head = u8RxBuff[FRAME_HEAD_INDEX] + (u8RxBuff[FRAME_HEAD_INDEX + 1] << 8);
        if ((FRAME_HEAD == u16Head) &&
            (Ok == Modem_RecvData((uint8_t *)&u8RxBuff[FRAME_NUM_INDEX], 2, u32Timeout)))
        {
            if ((u8RxBuff[FRAME_NUM_INDEX] == (u8RxBuff[FRAME_XORNUM_INDEX] ^ FRAME_NUM_XOR_BYTE)) &&
                (Ok == Modem_RecvData(&u8RxBuff[FRAME_LENGTH_INDEX], 2, u32Timeout)))
            {
                u16PacketSize = u8RxBuff[FRAME_LENGTH_INDEX] + (u8RxBuff[FRAME_LENGTH_INDEX + 1] << 8);
                if (((u16PacketSize >= PACKET_MIN_SIZE) && (u16PacketSize <= PACKET_MAX_SIZE)) &&
                    (Ok == Modem_RecvData(&u8RxBuff[FRAME_PACKET_INDEX], u16PacketSize + 2, u32Timeout)))
                {
                    u16Crc16 = u8RxBuff[FRAME_PACKET_INDEX + u16PacketSize] +
                               (u8RxBuff[FRAME_PACKET_INDEX + u16PacketSize + 1] << 8);
                    if (Calculate_CRC16(&u8RxBuff[FRAME_PACKET_INDEX], u16PacketSize) == u16Crc16)
                    {
                        *u16RxLength = u16PacketSize;
                        return Ok;
                    }
                }
            }
        }
    }

    return Error;
}

/**
 * @brief  Modem send frame
 * @param  [in] u8TxBuff                Pointer to the buffer which the data to be sent.
 * @param  [in] u16TxLength             Buffer length.
 * @retval None
 */
void Modem_SendFrame(uint8_t *u8TxBuff, uint16_t u16TxLength)
{
    uint16_t u16Crc16;

    u8TxBuff[FRAME_LENGTH_INDEX]     = u16TxLength & 0x00FF;
    u8TxBuff[FRAME_LENGTH_INDEX + 1] = u16TxLength >> 8;
    u16Crc16 = Calculate_CRC16(&u8TxBuff[FRAME_PACKET_INDEX], u16TxLength);
    u8TxBuff[FRAME_PACKET_INDEX + u16TxLength]     = u16Crc16 & 0x00FF;
    u8TxBuff[FRAME_PACKET_INDEX + u16TxLength + 1] = u16Crc16 >> 8;
    Modem_SendData(&u8TxBuff[0], FRAME_PACKET_INDEX + u16TxLength + 2);
}

/**
 * @brief  Modem process
 * @param  [in] u32Timeout              Communication timeout(ms)
 * @retval An en_result_t enumeration value:
 *           - Ok: Communication done
 *           - ErrorTimeout: Communication timeout
 */
en_result_t Modem_Process(uint32_t u32Timeout)
{
    en_result_t enRet;
    uint32_t u32TimeoutCnt = 0u, u32UpgradeFlag;
    uint16_t u16PacketSize;

    while (1)
    {
        if (Ok == Modem_RecvFrame(&u8FrameData[0], &u16PacketSize, FRAME_RECV_TIMEOUT))
        {
            u32TimeoutCnt = 0u;
            u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK;

            switch (u8FrameData[PACKET_CMD_INDEX])
            {
                case PACKET_CMD_HANDSHAKE:
                    /* Reserved */
                    break;
                case PACKET_CMD_APP_UPGRADE:
                    u32UpgradeFlag = *(__IO uint32_t *)APP_UPGRADE_ADDRESS;
                    EFM_Unlock();
                    if (0xFFFFFFFFul != u32UpgradeFlag)
                    {
                        EFM_SectorErase(APP_UPGRADE_ADDRESS);
                    }
                    enRet = EFM_ProgramWordRB(APP_UPGRADE_ADDRESS, APP_UPGRADE_FLAG);
                    EFM_Lock();
                    if (Ok != enRet)
                    {
                        u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_ERROR;
                    }
                    break;
                default:
                    u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_ERROR;
                    break;
            }
            Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SIZE);
            // Check upgrade event
            if ((PACKET_CMD_APP_UPGRADE == u8FrameData[PACKET_CMD_INDEX]) &&
                (PACKET_ACK_OK == u8FrameData[PACKET_RESULT_INDEX]))
            {
                /* Generate a software reset */
                NVIC_SystemReset();
            }
        }
        else
        {
            u32TimeoutCnt += FRAME_RECV_TIMEOUT;
        }
        /* Communication timeout */
        if (u32TimeoutCnt >= u32Timeout)
        {
            return ErrorTimeout;
        }
    }
}

/**
 * @}
 */

/**
 * @}
 */

/**
* @}
*/

/******************************************************************************
 * EOF (not truncated)
 *****************************************************************************/
